package jannex.mapping;

import jannex.internal.ReflectionUtils;
import jannex.sql.JxArray;
import jannex.sql.JxPreparedStatement;
import jannex.sql.JxResultSet;
import jannex.sql.TypedArray;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;

public class TypeHandlers {

    public static TypeHandler<Short> forShort() {
        return new TypeHandler<Short>() {
            @Override
            public Short getObject(JxResultSet resultSet, String columnName) {
                return resultSet.getShort(columnName);
            }

            @Override
            public void setObject(Short s, JxPreparedStatement statement, int parameterIndex) {
                statement.setInt(parameterIndex, s == null ? (short) 0 : s);
            }
        };
    }

    public static TypeHandler<Integer> forInt() {
        return new TypeHandler<Integer>() {
            @Override
            public Integer getObject(JxResultSet resultSet, String columnName) {
                return resultSet.getInt(columnName);
            }

            @Override
            public void setObject(Integer integer, JxPreparedStatement statement, int parameterIndex) {
                statement.setInt(parameterIndex, integer == null ? 0 : integer);
            }
        };
    }

    public static <T> TypeHandler<T> forObject() {
        return new TypeHandler<T>() {
            @Override
            @SuppressWarnings("unchecked")
            public T getObject(JxResultSet resultSet, String columnName) {
                return (T) resultSet.getObject(columnName);
            }
            @Override
            public void setObject(T object, JxPreparedStatement statement, int parameterIndex) {
                statement.setObject(parameterIndex, object);
            }
        };
    }


    public static TypeHandler<Object> forObject(final int type, final int scaleOrLength) {
        return new TypeHandler<Object>() {
            @Override
            public Object getObject(JxResultSet resultSet, String columnName) {
                return resultSet.getObject(columnName);
            }

            @Override
            public void setObject(Object object, JxPreparedStatement statement, int parameterIndex) {
                statement.setObject(parameterIndex, object, type, scaleOrLength);
            }
        };
    }

    public static <T> TypeHandler<Collection<T>> forList(Class<T> tClass) {
        return new CollectionTypeHandler<T>(tClass) {
            @Override
            public Collection<T> newCollection() {
                return new ArrayList<>();
            }
        };
    }

    public static <T> TypeHandler<Collection<T>> forSet(Class<T> tClass) {
        return new CollectionTypeHandler<T>(tClass) {
            @Override
            public Collection<T> newCollection() {
                return new HashSet<>();
            }
        };
    }

    public abstract static class CollectionTypeHandler<T> implements TypeHandler<Collection<T>> {
        private final Class<T> tClass;

        public CollectionTypeHandler(Class<T> tClass) {
            this.tClass = tClass;
        }

        public abstract Collection<T> newCollection();

        @Override
        public Collection<T> getObject(JxResultSet resultSet, String columnName) {
            @SuppressWarnings("unchecked")
            JxArray array = resultSet.getArray(columnName);
            T[] tArray = array.as(tClass).getArray();
            Collection<T> ts = newCollection();
            Collections.addAll(ts, tArray);
            return ts;
        }

        @Override
        public void setObject(Collection<T> objects, JxPreparedStatement statement, int parameterIndex) {
            if (objects == null) {
                statement.setObject(parameterIndex, null);
                return;
            }
            T[] ts = ReflectionUtils.castArray(tClass, objects.toArray());
            try (TypedArray<T> array = statement.getConnection().createArrayOf(ts)) {
                statement.setArray(parameterIndex, array);
            }
        }
    }
}
